/**
 * @file   testTypeList.cpp
 * @author wolfhead <wolfhead@wolfhead-laptop>
 * @date   Wed Sep 21 13:30:12 2011
 * 
 * @brief  
 * 
 * 
 */
#include "gtest/gtest.h"
#include <acapella/tools.hpp>
#include <acapella/typelist/typelist.hpp>
#include <acapella/typelist/typelist_macro_generator.hpp>

namespace
{
    class A
    {
    public:
        static std::string test(){return "A";}
    };

    class B : public A
    {
    public:
        static std::string test(){return "B";}
    };
            
    class C : public B
    {
    public:
        static std::string test(){return "C";}
    };
                
    class D : public A
    {
    public:
        static std::string test(){return "D";}
    };
}

using namespace acapella;

TEST(TypelistTest, GeneratorTest)
{
    //acapella::TL::generator::generate_TYPELIST(50);
}

TEST(TypelistTest, CompileTest)
{
    TYPELIST_1(char) test;
    (void)test;
}

TEST(TypelistTest, LengthTest)
{
    int value = TL::Length<TYPELIST_3(int, int ,int)>::value;

    EXPECT_EQ(3, value);
}


TEST(TypelistTest, TypeAtTest)
{
    typedef TL::TypeAt<TYPELIST_2(char, int), 1>::Result Result1;
    typedef TL::TypeAt<TYPELIST_2(char, int), 0>::Result Result2;

    EXPECT_EQ(true, SAMETYPE(int, Result1));
    EXPECT_EQ(false, (bool)SAMETYPE(int, Result2));
}

TEST(TypelistTest, TypeAtNonStrictTest)
{
    typedef TL::TypeAtNonStrict<TYPELIST_2(char, int), 1, NullType>::Result Result1;
    typedef TL::TypeAtNonStrict<TYPELIST_2(char, int), 0, NullType>::Result Result2;
    typedef TL::TypeAtNonStrict<TYPELIST_2(char, int), 2, NullType>::Result Result3;

    EXPECT_EQ(true, SAMETYPE(int, Result1));
    EXPECT_EQ(true, SAMETYPE(char, Result2));
    EXPECT_EQ(true, SAMETYPE(NullType, Result3));
}

TEST(TypelistTest, IndexOfTest)
{
    int value = TL::IndexOf<TYPELIST_4(char, unsigned char, int, unsigned int), unsigned int>::value;

    EXPECT_EQ(3, value);
}

TEST(TypelistTest, AppendTypeTest)
{
    typedef TYPELIST_4(char, unsigned char, int, unsigned int) List1;
    typedef TL::Append<List1, double>::Result Result1;

    int value = TL::IndexOf<Result1, double>::value;

    EXPECT_EQ(4, value);


    typedef TYPELIST_4(char, unsigned char, int, unsigned int) List2;
    typedef TL::Append<List1, TYPELIST_2(double, long double)>::Result Result2;

    value = TL::IndexOf<Result2, long double>::value;

    EXPECT_EQ(5, value);
}

TEST(TypelistTest, EraseTest)
{
    typedef TYPELIST_4(char, unsigned char, int, unsigned int) List1;
    int value = TL::IndexOf<List1, int>::value;

    EXPECT_EQ(2, value);

    typedef TL::Erase<List1, char>::Result List2;
    value = TL::IndexOf<List2, int>::value;

    EXPECT_EQ(1, value);

    typedef TL::Erase<List2, int>::Result List3;
    value = TL::IndexOf<List3, int>::value;

    EXPECT_EQ(-1, value);
}

TEST(TypelistTest, EraseAllTest)
{
    typedef TL::EraseAll<TYPELIST_3(int, int ,int), int>::Result List1;
    int value = TL::Length<List1>::value;

    EXPECT_EQ(0, value);
}

TEST(TypelistTest, NoDuplicateTest)
{
    typedef TL::NoDuplicate<TYPELIST_5(int, int ,int, double, double)>::Result List1;
    int value = TL::Length<List1>::value;

    EXPECT_EQ(2, value);
}

TEST(TypelistTest, ReplaceTest)
{
    typedef TYPELIST_5(int, int ,int, double, double) List1;
    int value = TL::IndexOf<List1, double>::value;

    EXPECT_EQ(3, value);

    typedef TL::Replace<List1, double, int>::Result List2;
    value = TL::IndexOf<List2, double>::value;

    EXPECT_EQ(4, value);   
}


TEST(TypelistTest, ReplaceAllTest)
{
    typedef TYPELIST_5(int, int ,int, double, double) List1;
    int value = TL::IndexOf<List1, double>::value;

    EXPECT_EQ(3, value);

    typedef TL::ReplaceAll<List1, double, int>::Result List2;
    value = TL::IndexOf<List2, double>::value;

    EXPECT_EQ(-1, value);   
}




TEST(TypelistTest, MostDerivedTest)
{
    typedef TYPELIST_3(A, B, C) List1;
    
    typedef TL::MostDerived<List1, A>::Result Result;

    EXPECT_EQ("C", Result::test());
}


TEST(TypelistTest, DerivedToFrontTest)
{
    typedef TYPELIST_4(A, B, D, C) List1;
    
    typedef TL::DerivedToFront<List1>::Result List2;

    printf("%s\n", TL::TypeAt<List2, 0>::Result::test().c_str());
    printf("%s\n", TL::TypeAt<List2, 1>::Result::test().c_str());
    printf("%s\n", TL::TypeAt<List2, 2>::Result::test().c_str());
    printf("%s\n", TL::TypeAt<List2, 3>::Result::test().c_str());
    
}
